/*------------------------------------------------------------------------/
 /  Universal string handler for user console interface
 /-------------------------------------------------------------------------/
 /
 /  Copyright (C) 2011, ChaN, all right reserved.
 /
 / * This software is a free software and there is NO WARRANTY.
 / * No restriction on use. You can use, modify and redistribute it for
 /   personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
 / * Redistributions of source code must retain the above copyright notice.
 /
 /-------------------------------------------------------------------------*/

#include "xprintf.h"

#if _USE_XFUNC_OUT
#include <stdarg.h>
void (*xfunc_out)(unsigned char); /* Pointer to the output stream */
static char *outptr;
static void xvprintf(const char* fmt, va_list arp);

/*----------------------------------------------*/
/* Debug message printing                       */
/*----------------------------------------------*/

// A macro to allow tracing of all debug events.
#define DEBUG_MARKER() do { \
	debug ("%s()   at line %d.\n", __func__, __LINE__); \
	} while (0)
		//debugEvent(__LINE__);

// DEBUG_SELECTED can be the sum of all the enabling codes
// e.g. DEBUG_SELECTED GUI + GUI_DETAIL + USB etc.
// uncomment one of the following lines or create your own custom set

//#define DEBUG_SELECTED	(GUI + TOUCH)
//#define DEBUG_SELECTED	(LCD + ENCODER)
//#define DEBUG_SELECTED	(GUI + CAL + TOUCH + LCD + ENCODER)
#define DEBUG_SELECTED NONE

void debug (int debug_code,

		/* Put a formatted string to the default device */
		const char* fmt, /* Pointer to the format string */
		... /* Optional arguments */



)
{
	if ((debug_code & DEBUG_SELECTED) != NONE) {
		if (debug_code & GUI)
			xprintf ("Debug GUI: ");
		if (debug_code & GUI_DETAIL)
			xprintf ("Debug GUI detail: ");
		if (debug_code & USB)
			xprintf ("Debug USB: ");
		if (debug_code & CAL)
			xprintf ("Debug calibrate: ");
		if (debug_code & TOUCH)
			xprintf ("Debug touch display: ");
		if (debug_code & LCD)
			xprintf ("Debug LCD: ");
		if (debug_code & ENCODER)
			xprintf ("Debug Encoder: ");
        // Call the xprintf using the var-args
        va_list arp;
        va_start(arp, fmt);
        xvprintf(fmt, arp);
        va_end(arp);
	}
}

/*----------------------------------------------*/
/* Put a character                              */
/*----------------------------------------------*/

void xputc(char c)
{
	if (_CR_CRLF && c == '\n')
		xputc('\r'); /* CR -> CRLF */

	if (outptr) {
		*outptr++ = (unsigned char) c;
		return;
	}

	if (xfunc_out)
		xfunc_out((unsigned char) c);
}

/*----------------------------------------------*/
/* Put a null-terminated string                 */
/*----------------------------------------------*/

void xputs( /* Put a string to the default device */
		const char* str /* Pointer to the string */
)
{
	while (*str)
		xputc(*str++);
}

void xfputs( /* Put a string to the specified device */
		void (*func)(unsigned char), /* Pointer to the output function */
		const char* str /* Pointer to the string */
)
{
	void (*pf)(unsigned char);

	pf = xfunc_out; /* Save current output device */
	xfunc_out = func; /* Switch output to specified device */
	while (*str) /* Put the string */
		xputc(*str++);
	xfunc_out = pf; /* Restore output device */
}

/*----------------------------------------------*/
/* Formatted string output                      */
/*----------------------------------------------*/
/*  xprintf("%d", 1234);			"1234"
 xprintf("%6d,%3d%%", -200, 5);	"  -200,  5%"
 xprintf("%-6u", 100);			"100   "
 xprintf("%ld", 12345678L);		"12345678"
 xprintf("%04x", 0xA3);			"00a3"
 xprintf("%08LX", 0x123ABC);		"00123ABC"
 xprintf("%016b", 0x550F);		"0101010100001111"
 xprintf("%s", "String");		"String"
 xprintf("%-4s", "abc");			"abc "
 xprintf("%4s", "abc");			" abc"
 xprintf("%c", 'a');				"a"
 xprintf("%f", 10.0);            <xprintf lacks floating point support>
 */

static
void xvprintf(const char* fmt, /* Pointer to the format string */
		va_list arp /* Pointer to arguments */
)
{
	unsigned int r, i, j, w, f;
	unsigned long v;
	char s[16], c, d, *p;

	for (;;) {
		c = *fmt++; /* Get a char */
		if (!c)
			break; /* End of format? */
		if (c != '%') { /* Pass through it if not a % sequense */
			xputc(c);
			continue;
		}
		f = 0;
		c = *fmt++; /* Get first char of the sequense */
		if (c == '0') { /* Flag: '0' padded */
			f = 1;
			c = *fmt++;
		}
		else {
			if (c == '-') { /* Flag: left justified */
				f = 2;
				c = *fmt++;
			}
		}
		for (w = 0; c >= '0' && c <= '9'; c = *fmt++) /* Minimum width */
			w = w * 10 + c - '0';
		if (c == 'l' || c == 'L') { /* Prefix: Size is long int */
			f |= 4;
			c = *fmt++;
		}
		if (!c)
			break; /* End of format? */
		d = c;
		if (d >= 'a')
			d -= 0x20;
		switch (d) { /* Type is... */
		case 'S': /* String */
			p = va_arg(arp, char*);
			for (j = 0; p[j]; j++)
				;
			while (!(f & 2) && j++ < w)
				xputc(' ');
			xputs(p);
			while (j++ < w)
				xputc(' ');
			continue;
		case 'C': /* Character */
			xputc((char) va_arg(arp, int));
			continue;
		case 'B': /* Binary */
			r = 2;
			break;
		case 'O': /* Octal */
			r = 8;
			break;
		case 'D': /* Signed decimal */
		case 'U': /* Unsigned decimal */
			r = 10;
			break;
		case 'X': /* Hexdecimal */
			r = 16;
			break;
		default: /* Unknown type (passthrough) */
			xputc(c);
			continue;
		}

		/* Get an argument and put it in numeral */
		v = (f & 4) ?
				va_arg(arp, long) :
				((d == 'D') ?
						(long) va_arg(arp, int) :
						(long) va_arg(arp, unsigned int) );
		if (d == 'D' && (v & 0x80000000)) {
			v = 0 - v;
			f |= 8;
		}
		i = 0;
		do {
			d = (char) (v % r);
			v /= r;
			if (d > 9)
				d += (c == 'x') ? 0x27 : 0x07;
			s[i++] = d + '0';
		} while (v && i < sizeof(s));
		if (f & 8)
			s[i++] = '-';
		j = i;
		d = (f & 1) ? '0' : ' ';
		while (!(f & 2) && j++ < w)
			xputc(d);
		do
			xputc(s[--i]);
		while (i);
		while (j++ < w)
			xputc(' ');
	}
}

void xprintf( /* Put a formatted string to the default device */
		const char* fmt, /* Pointer to the format string */
		... /* Optional arguments */
)
{
	va_list arp;

	va_start(arp, fmt);
	xvprintf(fmt, arp);
	va_end(arp);
}

void xsprintf( /* Put a formatted string to the memory */
		char* buff, /* Pointer to the output buffer */
		const char* fmt, /* Pointer to the format string */
		... /* Optional arguments */
)
{
	va_list arp;

	outptr = buff; /* Switch destination for memory */

	va_start(arp, fmt);
	xvprintf(fmt, arp);
	va_end(arp);

	*outptr = 0; /* Terminate output string with a \0 */
	outptr = 0; /* Switch destination for device */
}

void xfprintf( /* Put a formatted string to the specified device */
		void (*func)(unsigned char), /* Pointer to the output function */
		const char* fmt, /* Pointer to the format string */
		... /* Optional arguments */
)
{
	va_list arp;
	void (*pf)(unsigned char);

	pf = xfunc_out; /* Save current output device */
	xfunc_out = func; /* Switch output to specified device */

	va_start(arp, fmt);
	xvprintf(fmt, arp);
	va_end(arp);

	xfunc_out = pf; /* Restore output device */
}

/*----------------------------------------------*/
/* Dump a line of binary dump                   */
/*----------------------------------------------*/

void put_dump(const void* buff, /* Pointer to the array to be dumped */
		unsigned long addr, /* Heading address value */
		int len, /* Number of items to be dumped */
		int width /* Size of the items (DF_CHAR, DF_SHORT, DF_LONG) */
)
{
	int i;
	const unsigned char *bp;
	const unsigned short *sp;
	const unsigned long *lp;

	xprintf("%08lX ", addr); /* address */

	switch (width) {
	case DW_CHAR:
		bp = buff;
		for (i = 0; i < len; i++) /* Hexdecimal dump */
			xprintf(" %02X", bp[i]);
		xputc(' ');
		for (i = 0; i < len; i++) /* ASCII dump */
			xputc((bp[i] >= ' ' && bp[i] <= '~') ? bp[i] : '.');
		break;
	case DW_SHORT:
		sp = buff;
		do /* Hexdecimal dump */
			xprintf(" %04X", *sp++);
		while (--len);
		break;
	case DW_LONG:
		lp = buff;
		do /* Hexdecimal dump */
			xprintf(" %08LX", *lp++);
		while (--len);
		break;
	}

	xputc('\n');
}

#endif /* _USE_XFUNC_OUT */

#if _USE_XFUNC_IN
unsigned char (*xfunc_in)(void); /* Pointer to the input stream */

/*----------------------------------------------*/
/* Get a line from the input                    */
/*----------------------------------------------*/

int xgets ( /* 0:End of stream, 1:A line arrived */
		char* buff, /* Pointer to the buffer */
		int len /* Buffer length */
)
{
	int c, i;

	if (!xfunc_in) return 0; /* No input function specified */

	i = 0;
	for (;;) {
		c = xfunc_in(); /* Get a char from the incoming stream */
		if (!c) return 0; /* End of stream? */
		if (c == '\r') break; /* End of line? */
		if (c == '\b' && i) { /* Back space? */
			i--;
			if (_LINE_ECHO) xputc(c);
			continue;
		}
		if (c >= ' ' && i < len - 1) { /* Visible chars */
			buff[i++] = c;
			if (_LINE_ECHO) xputc(c);
		}
	}
	buff[i] = 0; /* Terminate with a \0 */
	if (_LINE_ECHO) xputc('\n');
	return 1;
}

int xfgets ( /* 0:End of stream, 1:A line arrived */
		unsigned char (*func)(void), /* Pointer to the input stream function */
		char* buff, /* Pointer to the buffer */
		int len /* Buffer length */
)
{
	unsigned char (*pf)(void);
	int n;

	pf = xfunc_in; /* Save current input device */
	xfunc_in = func; /* Switch input to specified device */
	n = xgets(buff, len); /* Get a line */
	xfunc_in = pf; /* Restore input device */

	return n;
}

/*----------------------------------------------*/
/* Get a value of the string                    */
/*----------------------------------------------*/
/*	"123 -5   0x3ff 0b1111 0377  w "
 ^                           1st call returns 123 and next ptr
 ^                        2nd call returns -5 and next ptr
 ^                3rd call returns 1023 and next ptr
 ^         4th call returns 15 and next ptr
 ^    5th call returns 255 and next ptr
 ^ 6th call fails and returns 0
 */

int xatoi ( /* 0:Failed, 1:Successful */
		char **str, /* Pointer to pointer to the string */
		long *res /* Pointer to the valiable to store the value */
)
{
	unsigned long val;
	unsigned char c, r, s = 0;

	*res = 0;

	while ((c = **str) == ' ') (*str)++; /* Skip leading spaces */

	if (c == '-') { /* negative? */
		s = 1;
		c = *(++(*str));
	}

	if (c == '0') {
		c = *(++(*str));
		switch (c) {
		case 'x': /* hexdecimal */
			r = 16; c = *(++(*str));
			break;
		case 'b': /* binary */
			r = 2; c = *(++(*str));
			break;
		default:
			if (c <= ' ') return 1; /* single zero */
			if (c < '0' || c > '9') return 0; /* invalid char */
			r = 8; /* octal */
		}
	}
	else {
		if (c < '0' || c > '9') return 0; /* EOL or invalid char */
		r = 10; /* decimal */
	}

	val = 0;
	while (c > ' ') {
		if (c >= 'a') c -= 0x20;
		c -= '0';
		if (c >= 17) {
			c -= 7;
			if (c <= 9) return 0; /* invalid char */
		}
		if (c >= r) return 0; /* invalid char for current radix */
		val = val * r + c;
		c = *(++(*str));
	}
	if (s) val = 0 - val; /* apply sign if needed */

	*res = val;
	return 1;
}

#endif /* _USE_XFUNC_IN */
